#include <filesystem.h>
#include <util.h>

FileSystem::FileSystem()
{

}

void FileSystem::Format()
{
    //分配一个盘块作为根目录的八个目录项使用
    string empty_dire_item = "$       ";
    int root_dir = disk_->Write(empty_dire_item);

    for (size_t i = 0; i < 7; i ++) {
        disk_->AppendToBlock(root_dir, empty_dire_item);
    }

    root_dir_item_block_ = root_dir;
}

int FileSystem::NewFile(File &file) {
    //目录是否存在
    if (!ExistDir(file.path_)) return -1;
    
    //目录项空间是否足够
    if(GetEmptyDirItemIndex(file.path_) < 0) return -2;
    
    //计算文件大小
    file.size_ = ceil((double)file.content_.length()/BLOCK_SIZE);
    
    //磁盘空间是否足够
    
    //将 file.content 写入磁盘、记录开始盘块号
    file.start_block_ = disk_->Write(file.content_);
    
    //修改目录项
    file_map_[file.path_][GetEmptyDirItemIndex(file.path_)] = file;
    
    return file.start_block_;
   
}

vector<File> FileSystem:: GetDirItemInBlock(int block_id) {
    vector<int> codes = disk_->ReadCodeFromBlock(block_id);
    vector<File> files;
    string name, type;
    int attribute, start_block, size;
    for (size_t i = 0; i < BLOCK_SIZE; i += DIR_ITEM_SIZE) {
        File buf;
        if (codes[i] == '$') {
            buf.ChangeToEmptyDirItem();
        }
        else {
            name = "";
            type = "";
            attribute = 0;
            start_block = 0;
            size = 0;
            
            for (size_t j = i; j < i+3; j ++) {
                if (codes[j] != 0) {
                    name += (char)codes[j];
                }
            }
            buf.name_ = name;
            for (size_t j = i+3; j < i+5; j ++) {
                if (codes[j] != 0) {
                    type += (char)codes[j];
                }
            }
            buf.type_ = type;
            buf.attribute_ = codes[i+5];
            buf.start_block_ = codes[i+6];
            buf.size_ = codes[i+7];
        }
        files.push_back(buf);
    }

    return files;
}

vector<File> FileSystem::GetRootDirItem() {
    return GetDirItemInBlock(root_dir_item_block_);
}

int FileSystem::AddDirItemToBlock(int block_id, File dir_item) {
    vector<File> items = GetDirItemInBlock(block_id);
    bool success = false;
    int index_itr = 0;
    for (auto &i : items) {
        if (i.name_ == "$") {
            i = dir_item;
            success = true;
            break;
        }
        index_itr ++;
    }
    if (!success) return 0;
    SyncDirItemToDisk(block_id, &items);
    return index_itr;
}

bool FileSystem::SyncDirItemToDisk(int block_id, vector<File> *dir_items) {
    vector<int> codes;
    unsigned int i;
    int char_buf = 0;
    for(auto &item : *dir_items) {
        if (item.name_ == "$") {
            codes.push_back('$');
            for (i = 0; i < 7; i++) {
                codes.push_back(' ');
            }
            continue;
        }
        
        for (i = 0; i < 3; i++) {
            if (i < item.name_.length()) {
                char_buf = (int)((unsigned char)item.name_[i]);
                codes.push_back(char_buf);
            }
            else {
                codes.push_back(0);
            }
        }
        for (i = 0; i < 2; i++) {
            if (i < item.type_.length()) {
                char_buf = (int)((unsigned char)item.type_[i]);
                codes.push_back(char_buf);
            }
            else {
                codes.push_back(0);
            }
        }
        codes.push_back(item.attribute_);
        codes.push_back(item.start_block_);
        codes.push_back(item.size_);
    }
    disk_->WriteCodeToBlock(block_id, codes);
    return true;
}

int FileSystem::GetNumOfDirItemInBlock(int block_id) {
    vector<File> dir_items = GetDirItemInBlock(block_id);

    int empty_block = 0;

    for (auto &item : dir_items) {
        if (item.name_ == "$") {
            empty_block ++;
        }
    }

    return BLOCK_SIZE/DIR_ITEM_SIZE - empty_block;
}

int FileSystem::NewDir(File file) {
    file.type_ = "";
    if (file.attribute_ != 0xC) {
        return 0; //类型错误
    }
    for (size_t i = 1; i <= BLOCK_SIZE/DIR_ITEM_SIZE; i ++) {
        file.content_ = file.content_ + "$       ";
    }
    NewFile(file);
    if (file.path_[file.path_.length()-1] == '/')
        file_map_[file.path_ + file.name_] = GetDirItemInBlock(file.start_block_);
    else {
        file_map_[file.path_ + "/" + file.name_] = GetDirItemInBlock(file.start_block_);
    }
    return file.start_block_;
}

void FileSystem::InitFileMap() {
    file_map_["/"] = GetRootDirItem();
    
    // 待扫描的目录队列
    std::queue<File> q;
    
    // 扫描根目录
    for (auto &item : file_map_["/"]) {
        item.path_ = "/";
        
        if (item.name_ != "$" && item.attribute_ == 0xC) {
            q.push(item);
        }
    }

    vector<File> buffer;
    
    // 扫描目录
    while (!q.empty()) {
        buffer = GetDirItemInBlock((q.front()).start_block_);
        for (auto &item : buffer) {
            item.path_ = q.front().GetPathName();
            if (item.name_ != "$" && item.attribute_ == 0xC) {
                q.push(item);
            }
        }
        
        file_map_[q.front().GetPathName()] = buffer;
        q.pop();
    }
}

bool FileSystem::ExistDir(string path) {
    return file_map_.find(path) != file_map_.end(); 
}

bool FileSystem::ExistFile(File file) {
    if (!ExistDir(file.path_)) {
        return false;
    }
    
    for (auto &f : file_map_[file.path_]) {
        if (file.name_ == f.name_) {
            return true;
        }
    }
    
    return false;
}

int FileSystem::GetEmptyDirItemIndex(string path) {
    if (!ExistDir(path)) return false;
    for (size_t i = 0; i < file_map_[path].size(); i ++) {
        if (file_map_[path][i].name_ == "$") return i;
    }
    return -1;
}

bool FileSystem::DeleteFile(File file) {
    if (!ExistFile(file)) return false;
   
    // 如果是目录则从目录项缓存中删除他
    if (file.IsDir()) {
        file_map_.erase(file.GetPathName());
    }
    
    //删除内容
    if (file.start_block_ >= 4)
        disk_->Clear(file.start_block_);
    
    //删除在父目录中的目录项
    vector<File>* dir_items = &file_map_[file.path_]; 
    (*dir_items)[FindIndexInDirByName(file.path_, file.GetNameType())].ChangeToEmptyDirItem();
    
    return true; 
}

int FileSystem::FindIndexInDirByName(string path, string name) {
    unsigned int point_index = name.find('.');
    string type = "";
    
    if (point_index != name.npos) {
        type = name.substr(point_index + 1, name.length() - point_index - 1);
        name = name.substr(0, point_index);
    }
    
    if (!ExistDir(path)) return -1;
    vector<File> dir_items = file_map_[path];
    for(size_t i = 0; i < dir_items.size(); i ++) {
        if (dir_items[i].name_ == name && type == dir_items[i].type_) {
            return i;
        }
    }
    return -1;
}

void FileSystem::Flush() {
    
    SyncDirItemToDisk(root_dir_item_block_, &file_map_["/"]);
    
    
    //将 file_map_ 写入磁盘中
    string parent_dir;
    int parent_dir_length;
    for (auto &items : file_map_) {
        
        if (items.first == "/") continue;

        // 从上级目录的目录项中找到当前目录项所在的盘块 
        // 获取上级目录
        for (int i = items.first.length()-1; i >= 0 && items.first[i] != '/'; i --) {
            parent_dir_length = i;
        }
        
        if (parent_dir_length == 1){
            parent_dir = items.first.substr(0, parent_dir_length);
        }        
        else {
            parent_dir =items.first.substr(0, parent_dir_length - 1);
        }
        //当前目录的目录项
        string name = File::GetName(items.first);
        File cur_dir_item = file_map_[parent_dir][FindIndexInDirByName(parent_dir, name)];
        SyncDirItemToDisk(cur_dir_item.start_block_, &items.second);
    }
    
    disk_->Sync();
}

string FileSystem::ReadFile(File &file) {
    if (file.start_block_ == 0) {
        return "";
    }
    file.content_ = disk_->Read(file.start_block_);
    return file.content_;
}

bool FileSystem::UpdateFile(File &file) {
    if (file.content_ == "") {
        if (file.start_block_ != 0) {
            disk_->Clear(file.start_block_);
            file.start_block_ = 0;
        }
        return true;
    }
    if (file.start_block_ != 0)
        disk_->Update(file.start_block_, file.content_);
    else {
        file.start_block_ = disk_->Write(file.content_);
    }
    return true;
}

bool FileSystem::ReadOpeningFile() {
    opening_file_->content_ = ReadFile(*opening_file_);
    return true;
}

bool FileSystem::OpenFile(string path_name) {
    string parent_dir = File::GetParentDir(path_name);
    string name = File::GetName(path_name);
    int index = FindIndexInDirByName(parent_dir, name);
    if (index == -1) return false;
    opening_file_ = &file_map_[parent_dir][index];
    opening_path_ = parent_dir;
    ReadOpeningFile();
    return true;
}

bool FileSystem::OpenDir(string path) {
    if (!ExistDir(path)) return false;
    opening_path_ = path;
    opening_file_ = nullptr;
    
    return true;
}

std::vector<File>* FileSystem::GetDirItems(string path) {
    if (!ExistDir(path)) return nullptr;
    return &file_map_[path];
}

File* FileSystem::GetFileDirItemPtr(string path_name) {
    string parent_dir = File::GetParentDir(path_name);
    string name = File::GetName(path_name);
    int index = FindIndexInDirByName(parent_dir, name);
    if (index == -1) return nullptr;
    return &file_map_[parent_dir][index];
}

void FileSystem::CloseOpeningFile() {
    if (opening_file_ != nullptr) {
        // 清除内容缓存
        opening_file_->content_ = "";
        opening_file_ = nullptr;
    }
}

string FileSystem::GetAllCodesInDisk() {
    Flush();
    disk_->GetFstream()->seekg(0, std::ios::beg);
    std::string str((std::istreambuf_iterator<char>(*disk_->GetFstream())),
                     std::istreambuf_iterator<char>());

    return str;
}

void FileSystem::LoadFileContent(File* file) {
    if (file->IsDir()) {
        SyncDirItemToDisk(file->start_block_, &file_map_[file->GetPathName()]);
    }
    file->content_ = disk_->Read(file->start_block_);
}

bool FileSystem::MoveFileOrDir(File* file_, string new_path) {
    // 加载内容
    this->LoadFileContent(file_);
    // 新建文件
    File file(file_->name_, new_path);
    file.attribute_ = file_->attribute_;
    file.type_ = file_->type_;
    file.content_ = file_->content_;
    
    string old_path = file_->path_;
    if (file_->IsDir()) {
        // 修改目录项
        ModifyAllPathInDir(file_, new_path);
    }
    file_->path_ = old_path;
    // 删除原文件
    this->DeleteFile(*file_);
    this->NewFile(file);
    return true;
}

/**
 * @brief FileSystem::ModifyAllPathInDir
 * @param dir 当前目录（要修改这个目录和这个目录内文件为新的路径）
 * @param path 当前目录要去的新的路径
 * @return 
 */
bool FileSystem::ModifyAllPathInDir(File* dir, string path) {
    vector<File>* dir_items = GetDirItems(dir->GetPathName());
    
    // 目录的原路径
    string old_path = dir->path_; 
    
    // 子文件的原路径、当前目录原目录项键值
    string sub_old_path = dir->GetPathName();
    
    // 子文件的新路径
    dir->path_ = path;
    string new_path = dir->GetPathName();
    
    //f 目录中本来包含的目录项
    for (File &f : *dir_items) {
        if (f.IsEmpty()) continue; 
        if (f.IsDir()) {
            // 修改 value 后再修改 key
            ModifyAllPathInDir(&f, new_path);
        }
        else {
            f.path_ = new_path;
        }
    }
    
    file_map_[dir->GetPathName()] = file_map_[sub_old_path];
    file_map_.erase(sub_old_path);
    return true;
}

